#include <iostream>
#include <sstream>
#include "debug.h"
#include "PatternManager.h"
#include "Gomoku.h"
#include "Arbiter.h"

Arbiter::Arbiter()
: m_currentPlayerWins(false), m_ruleThree(false)
{
}

Arbiter::~Arbiter()
{
}

static BoardCase createAdvColor(BoardCase _color)
{
	return ((_color == COLOR_BLACK) ? COLOR_WHITE : COLOR_BLACK);
}

static bool checkFivePiecesCompromised(int _x, int _y, BoardCase const& _case, Board& _board, bool _verbose = false)
{
	int nbDirections = 8;

	BoardCase** cases = _board.Cases;
	BoardCase align = GET_ALIGN_ENMY(_case);
	/*if (align == ENMY_NONE)
		return (false);*/
	for (int i = 0; i < nbDirections; i++)
	{
		BoardCase* ennemy = NULL;
		BoardCase directionEnnemy = Board::ConvertDirectionToEnnemy(i + 1);
		BoardCase directionAlly = Board::ConvertDirectionToAlly(i + 1);
		if (align & directionEnnemy)
		{
			BoardCase opDirection = Board::ConvertDirectionToAlly(Board::GetOppositeDirection(i + 1));
			BoardCase opDirectionEnmy = Board::ConvertDirectionToEnnemy(Board::GetOppositeDirection(i + 1));
			if (GET_ALIGN_ALLY(_case) & opDirection)
			{
				int offX = Board::GetXIncrementFromDirection(Board::GetOppositeDirection(i + 1));
				int offY = Board::GetYIncrementFromDirection(Board::GetOppositeDirection(i + 1));
				BoardCase* lastOne = &cases[_y + offY][_x + offX];
				if (_x + offX + offX <= 0 || _x + offX + offX >= _board.Width || _y + offY + offY <= 0 || _y + offY + offY >= _board.Height)
					continue ;
				if (GET_ALIGN_ALLY(*lastOne) & opDirection || GET_ALIGN_ENMY(*lastOne) & opDirectionEnmy)
					continue ;
				if (_verbose == true)
				{
#pragma region Affichage debuggage
#ifdef _DEBUG
					{
						std::stringstream oss;
						oss << "Five pieces compromised in {"; oss << _x;
						oss << ", "; oss << _y; oss << "}(1)";
						COUT_ENDL(oss.str());
					}
#endif
#pragma endregion
				}
				return (true);
			}
		}
		else if (GET_ALIGN_ALLY(_case) & directionAlly)
		{
			int off_x = Board::GetXIncrementFromDirection(Board::ConvertAllyToDirection(directionAlly));
			int off_y = Board::GetYIncrementFromDirection(Board::ConvertAllyToDirection(directionAlly));
			int x = _x + off_x;
			int y = _y + off_y;

			if (x >= 0 && x < _board.Width && y >= 0 && y < _board.Height)
			{
				BoardCase* ally = &cases[y][x];
				x = _x + Board::GetXIncrementFromDirection(i + 1);
				y = _y + Board::GetXIncrementFromDirection(i + 1);
				if (!(x >= 0 && x < _board.Width && y >= 0 && y < _board.Height))
					continue ;
				if (GET_ALIGN_ENMY(*ally) & directionEnnemy)
				{
					int test = Board::GetOppositeDirection(i + 1);
					BoardCase opDirection = Board::ConvertDirectionToAlly(Board::GetOppositeDirection(i + 1));
					BoardCase opDirectionEnmy = Board::ConvertDirectionToEnnemy(Board::GetOppositeDirection(i + 1));
					off_x = Board::GetXIncrementFromDirection(Board::GetOppositeDirection(i + 1));
					off_y = Board::GetYIncrementFromDirection(Board::GetOppositeDirection(i + 1));
					if (GET_ALIGN_ALLY(_case) & opDirection || GET_ALIGN_ENMY(_case) & opDirectionEnmy)
						continue ;
					else if (_x + off_x <= 0 || _x + off_x >= _board.Width || _y + off_y <= 0 || _y + off_y >= _board.Height)
						continue ;
					if (_verbose == true)
					{
#pragma region Affichage debuggage
#ifdef _DEBUG
						{
							std::stringstream oss;
							oss << "Five pieces compromised in {"; oss << _x;
							oss << ", "; oss << _y; oss << "}(2)";
							COUT_ENDL(oss.str());
						}
#endif
#pragma endregion
					}
					return (true);
				}
			}
			else
				continue ;
		}
	}

	return (false);
}

bool	Arbiter::CanICapture(Board& _board, BoardCase& _case, int _direction, std::list<BoardCase*>& _pieces)
{
	int _x = 0, _y = 0;
	Board::GetCasePosition(_case, _x, _y);
	BoardCase direction = Board::ConvertDirectionToEnnemy(_direction);
	if (GET_ALIGN_ENMY(_case) & direction)
	{
		BoardCase* adjacent = _board.GetAdjacentCase(_x, _y, _direction);
		if (adjacent == NULL)
			return (false);
		BoardCase allyDirection = Board::ConvertDirectionToAlly(_direction);
		if (GET_ALIGN_ALLY(*adjacent) & allyDirection)
		{
			int offx = Board::GetXIncrementFromDirection(_direction);
			int offy = Board::GetYIncrementFromDirection(_direction);
			int x = _x + offx;
			int y = _y + offy;
			BoardCase* lastOne = _board.GetAdjacentCase(x, y, _direction);
			if (lastOne == NULL)
				return (false);
			if (*lastOne & direction)
			{
				_pieces.push_back(adjacent);
				_pieces.push_back(lastOne);
				return (true);
			}
		}
	}
	return (false);
}

bool		Arbiter::IsFivePieceAligned(BoardCase& _case, Board& _board, bool _verbose)
{
	int _x = 0, _y = 0;
	Board::GetCasePosition(_case, _x, _y);
	int nbDirections = 8;
	BoardCase** cases = _board.Cases;

	for (int i = 0; i < nbDirections; i++)
	{
		int align = GET_ALIGN_ALLY(_case);
		if (align == ALLY_NONE)
			break ;

		BoardCase direction = Board::ConvertDirectionToAlly(i + 1);
		// TODO: Mettre les deux while en fonction...
		BoardCase const* myCase = &_case;
		int x = _x, y = _y;
		int count = 1;
		while (GET_ALIGN_ALLY(*myCase) & direction && count != 5)
		{
			int off_x = Board::GetXIncrementFromDirection(Board::ConvertAllyToDirection(direction));
			int off_y = Board::GetYIncrementFromDirection(Board::ConvertAllyToDirection(direction));

			if (checkFivePiecesCompromised(x, y, *myCase, _board))
				return (false);

			x += off_x;
			y += off_y;

			if (x >= 0 && x < _board.Width && y >= 0 && y < _board.Height)
			{
				myCase = &cases[y][x];
				count++;
			}
			else
				break ;
		}
		if (count == 5)
			return (true);
		if (myCase != &_case)
		{
			BoardCase opDirection = Board::ConvertDirectionToAlly(Board::GetOppositeDirection(i + 1));
			myCase = &cases[_y][_x];
			x = _x;
			y = _y;
			while (GET_ALIGN_ALLY(*myCase) & opDirection && count != 5)
			{
				int off_x = Board::GetXIncrementFromDirection(Board::ConvertAllyToDirection(opDirection));
				int off_y = Board::GetYIncrementFromDirection(Board::ConvertAllyToDirection(opDirection));

				if (checkFivePiecesCompromised(x, y, *myCase, _board))
					return (false);

				x += off_x;
				y += off_y;
				if (x >= 0 && x < _board.Width && y >= 0 && y < _board.Height)
				{
					myCase = &cases[y][x];
					count++;
				}
				else
					break ;
			}
			if (count == 5)
				return (true);
		}
	}
	return (false);
}

bool		Arbiter::IsDestinationEmpty(const int & dest_x, const int & dest_y, Board& _board) const
{
	BoardCase** cases = _board.Cases;
	BoardCase tmp = cases[dest_y][dest_x];
	bool res = (GET_COLOR(cases[dest_y][dest_x]) != COLOR_NONE) ? false : true;
	return (res);
}

bool		Arbiter::CanIMove(const int & dest_x, const int & dest_y, BoardCase& _case, Board& _board, bool _verbose)
{
	int direction = 0;
	BoardCase color = GET_COLOR(_case);
	if (Gomoku::Instance().RuleThreeUse == true)
	{
		std::list<pattern*>::const_iterator it = PatternManager::Instance().Patterns.begin();
		for (; it != PatternManager::Instance().Patterns.end(); ++it)
		{
			if (PatternManager::Instance().CheckPattern(*it, dest_x, dest_y, color, _board, _verbose))
			{
				m_ruleThree = true;
				return (false);
			}
			else
				m_ruleThree = false;
		}
	}
	if (!this->IsDestinationEmpty(dest_x, dest_y, _board))
		return (false);
	return (true);
}

bool		Arbiter::HasCurrentPlayerWon() const
{
	return (m_currentPlayerWins);
}

void		Arbiter::SetCurrentPlayerWin(bool _value)
{
	m_currentPlayerWins = _value;
}

bool Arbiter::RuleThree()
{
	return (m_ruleThree);
}

void Arbiter::SetRuleThree(bool _value)
{
	m_ruleThree = _value;
}
